
import pandas as pd
from typing import Optional, Annotated, List
from sqlmodel import select, Session, Field, SQLModel, Column
from sqlalchemy.dialects.postgresql import JSON
from fastapi import Depends, Header
from fastapi.responses import JSONResponse
from pydantic import BaseModel, field_validator, field_serializer

from utils.config import settings
from utils.response_utils import response_value
from utils.sysutils import get_password_hash, verify_password
from datetime import datetime, timedelta, timezone
import jwt
from jwt.exceptions import InvalidTokenError
from jwt import ExpiredSignatureError
from dependencies.database import SessionDep
from fastapi import status
import logging

logger = logging.getLogger(__name__)


# 定义 Token 模型，用于返回访问令牌
class Token(BaseModel):
    access_token: str  # 访问令牌字符串
    token_type: str  # 令牌类型，通常是 'bearer'


# 定义 TokenData 模型，用于解析 JWT 令牌中的数据
class TokenData(BaseModel):
    username: str | None = None  # 用户名，可选字段


# 公共类 用于返回信息
class UserPublic(SQLModel):
    id: Optional[int] = Field(default=None, primary_key=True, description="用户ID")
    username: str = Field(description="用户名", unique=True, index=True)
    # Optional[int] 表示该字段可以是 int 类型或 None
    age: Optional[int] = Field(default=None, description="用户年龄")
    gender: int = Field(default=1, description="性别 (1: 男, 2: 女)")
    idCard: str = Field(default="", description="身份证号")
    email: str = Field(default="", description="邮箱地址")
    address: str = Field(default="", description="地址")
    detail: str = Field(default="", description="用户详细信息")
    createTime: datetime = Field(default_factory=datetime.now, description="创建时间")
    status: int = Field(default=1, description="状态 (0: 禁用, 1: 启用)")
    avatar: str = Field(default="", description="头像URL")
    photo: List[dict] = Field(
        default_factory=list,
        sa_column=Column(JSON()),
        description="用户照片列表，包含 name 和 url 字段"
    )

    # 计算属性：将 gender 转换为描述
    @property
    def gender_str(self) -> str:
        gender_mapping = {1: "男", 2: "女"}
        return gender_mapping.get(self.gender, "未知")

    # 计算属性：将 status 转换为描述
    @property
    def status_str(self) -> str:
        status_mapping = {0: "禁用", 1: "启用"}
        return status_mapping.get(self.status, "未知")


# User带密码的数据库操作类，创建、更新、删除、查询等操作
class UserDB(UserPublic, table=True):
    password: str = Field(description="密码")

    @field_validator("photo", mode="before")
    def normalize_photo_data(cls, value):
        """统一处理不同格式的 photo 数据"""
        if isinstance(value, list):
            # 处理字符串格式（兼容旧数据）
            if all(isinstance(item, str) for item in value):
                return [{"url": url} for url in value]

            # 处理字典格式（带 url 的）
            if all(isinstance(item, dict) and "url" in item for item in value):
                return [{"url": item["url"], "name": item.get("name")} for item in value]

        # 无效格式返回空列表
        return []

    def __init__(self, **data):
        super().__init__(**data)
        if 'password' in data:
            self.password = get_password_hash(data['password'])  # 确保密码被哈希


# 定义 get_current_user 函数，用于从 JWT 令牌中获取当前用户信息
async def get_current_user(
        authorization: Annotated[str, Header(alias="Authorization")],
        db: SessionDep
) -> UserDB | dict:
    """
    从 JWT 令牌中解码并获取当前用户信息（包含过期检查）
    """
    try:
        scheme, token = authorization.strip().split()
        if scheme.lower() != "bearer":
            return response_value(code=status.HTTP_401_UNAUTHORIZED, msg="凭证无效或已过期")

        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])

        # 检查过期时间
        expire_timestamp = payload.get("exp")
        if expire_timestamp and datetime.now(timezone.utc).timestamp() > expire_timestamp:
            return response_value(code=status.HTTP_401_UNAUTHORIZED, msg="Token已过期")

        username: str = payload.get("sub")
        if not username:
            return response_value(code=status.HTTP_401_UNAUTHORIZED, msg="凭证无效或已过期")

        token_data = TokenData(username=username)
    except (ValueError, AttributeError, ExpiredSignatureError, InvalidTokenError, Exception) as e:
        logger.debug(f"认证失败: {str(e)}")
        return response_value(code=status.HTTP_401_UNAUTHORIZED, msg="凭证无效或已过期")

    user = get_user(db, username=token_data.username)
    if not user:
        return response_value(code=status.HTTP_401_UNAUTHORIZED, msg="凭证无效或已过期")

    return user


async def get_current_active_user(
        current_user: Annotated[UserDB | JSONResponse, Depends(get_current_user)]  # 接受可能返回的 JSONResponse
) -> UserDB | dict:
    """
    确保当前用户是活动用户
    get_current_active_user 的作用 get_current_active_user 是一个依赖函数，它的作用是：
        调用 get_current_user 获取当前用户信息。
        如果 get_current_user 返回 JSONResponse（表示错误），则直接透传该错误响应。
        如果 get_current_user 返回 UserDB 对象，则检查用户状态（status），如果用户未激活，返回错误响应；否则，返回用户对象。

    :param current_user: 值由 get_current_active_user 函数提供，可能是：
        UserDB 对象：表示当前用户信息。
        JSONResponse 对象：表示错误响应（例如凭证无效或用户未激活）。
    :return: 如果用户是活动用户，返回用户对象；否则返回错误响应
    """
    # 如果得到的是用户，就继续执行。如果得到的是错误，直接返回错误响应
    if isinstance(current_user, dict):  # 如果是错误响应，直接透传
        return current_user

    if not current_user.status:  # 如果用户未激活，返回错误响应
        return response_value(code=status.HTTP_401_UNAUTHORIZED, msg="用户已禁用")

    return current_user  # 正常返回用户对象


# 定义 authenticate_user 函数，用于验证用户名和密码
def authenticate_user(db: Session, username: str, password: str):
    user = get_user(db, username)
    if not user:
        logger.debug(f"数据库中找不到用户 {username}。")
        return False

    hashed_password_from_db = user.password
    logger.debug(f"用户 {username} 的数据库哈希密码: {hashed_password_from_db}")

    # 直接使用明文密码验证（verify_password内部会处理哈希对比）
    if not verify_password(password, hashed_password_from_db):
        logger.debug(f"用户 {username} 密码验证失败。")
        return False

    logger.debug(f"用户 {username} 密码验证成功。")
    return user


# 定义 get_user 函数，用于根据用户名从数据库中查询用户信息
def get_user(db: Session, username: str):
    """
    根据用户名从数据库中查询用户信息。

    :param db: 数据库会话对象
    :param username: 用户名
    :return: 查询到的用户对象，如果未找到则返回 None
    """
    # 使用 select 查询 UserDB 表，并添加 where 条件
    statement = select(UserDB).where(UserDB.username == username)
    user = db.exec(statement).first()
    return user


# 定义 create_access_token 函数，用于创建 JWT 访问令牌
def create_access_token(data: dict, expires_delta: timedelta | None = None):
    """
    创建 JWT 访问令牌。

    :param data: 要编码到令牌中的数据字典
    :param expires_delta: 令牌的有效期，默认为 15 分钟
    :return: 编码后的 JWT 字符串
    """
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.now(timezone.utc) + expires_delta
    else:
        expire = datetime.now(timezone.utc) + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
    # 将 datetime 对象转换为 Unix 时间戳（秒数）保持与gitee的token一致
    to_encode.update({"exp": int(expire.timestamp())})

    logger.debug(f"Encoding JWT with payload: {to_encode}")

    # to_encode 是要编码到令牌中的数据字典。SECRET_KEY 是用于签名的密钥。algorithm 是签名算法（例如 HS256）。
    encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
    logger.debug(f"Generated JWT token: {encoded_jwt}")
    return encoded_jwt
